"""
loguru 官方文档
https://loguru.readthedocs.io/en/stable/overview.html
"""
import logging
import os
import sys
from datetime import timedelta

import loguru

from app.core.config import settings
from app.utils import context


class InterceptHandler(logging.Handler):
    """
    日志拦截处理器：将所有 Python 标准日志重定向到 Loguru （用于处理uvicorn / fastapi 等自带的日志）

    参考：
    1.https://zhuanlan.zhihu.com/p/15448544324
    2.https://blog.csdn.net/kris__lee/article/details/144511413

    工作原理：
    1. 继承自 logging.Handler
    2. 重写 emit 方法处理日志记录
    3. 将标准库日志转换为 Loguru 格式
    """

    def emit(self, record: logging.LogRecord) -> None:
        """
        处理日志记录，将标准库日志重定向到Loguru

        :param record: 标准库日志记录对象
        """
        # 尝试获取日志级别
        try:
            level = logger.level(record.levelname).name
        except KeyError:
            level = record.levelno

        # 获取调用帧信息
        frame, depth = logging.currentframe(), 2
        while frame.f_code.co_filename == logging.__file__:
            frame = frame.f_back
            depth += 1

        # 使用loguru记录日志
        logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())


def _trace_log_format(record) -> str:
    """
    追踪日志格式化函数

    为日志添加trace_id信息，便于链路追踪和问题排查。

    :param record: 日志记录对象
    :return: 格式化后的日志字符串
    """
    trace_id = context.get_trace_id()
    if trace_id is None:
        record['extra']['traceId'] = ""
    else:
        record['extra']['traceId'] = f"[{trace_id}] "

    # 处理函数调用信息，给这个信息设置总宽度35.超长时截断。确保日志格式整齐统一
    location = f"{record['name']}:{record['function']}:{record['line']}"
    record['extra']['location'] = f"{location: <35.35}"

    return "<green>{time:YYYY-MM-DD HH:mm:ss,SSS}</green> " + \
        "<level>{level: <8}</level> --- {extra[traceId]}" + \
        "<cyan>{extra[location]}</cyan> : " + \
        "{message}\n{exception}"


def _add_other_handler():
    """
    添加第三方日志处理器

    将第三方库（如uvicorn、fastapi、sqlalchemy等）的日志重定向到Loguru统一管理。
    """
    logging.basicConfig(handlers=[InterceptHandler()], level=logging.NOTSET, force=True)

    for logger_name in [
        'uvicorn.error',
        'uvicorn.access',
        'uvicorn.asgi',
        'fastapi',
        'fastapi.error',
        'sqlalchemy',
        'sqlalchemy.engine',
        'sqlalchemy.pool',
        'sqlalchemy.dialects',
        'sqlalchemy.orm',
    ]:
        _logger = logging.getLogger(logger_name)
        _logger.handlers = [InterceptHandler()]
        _logger.propagate = False


class LoggerConfiguration:
    """ 日志配置类 """

    def __init__(self):
        # 日志对象
        _logger = loguru.logger
        _logger.remove()  # 清空默认的处理器

        self.logger = _logger

        # 添加控制台处理器
        self._add_console_handler()
        # 添加文件处理器
        self._add_file_handler()
        # 配置第三方日志库
        _add_other_handler()

    def _add_console_handler(self):
        """
        添加控制台处理器

        用于在控制台输出日志信息，方便开发调试。
        """
        self.logger.add(
            sink=sys.stdout,
            format=_trace_log_format,
            level=logging.DEBUG,
            backtrace=False,  # 禁用详细的回溯信息
            diagnose=False,  # 禁用诊断信息
            catch=True,
            enqueue=True
        )

    def _add_file_handler(self):
        """
        添加文件处理器

        将日志信息写入文件，按不同级别和用途分类存储。
        """
        # 检查输出目录是否存在，不存在则创建
        os.makedirs(name=settings.log_path, exist_ok=True)

        # 运行时日志, 记录所有DEBUG及以上级别的日志
        self.logger.add(
            sink=f"{settings.log_path}/app.log",
            format=_trace_log_format,
            level=logging.DEBUG,
            rotation=settings.log_rotation,
            retention=timedelta(days=settings.log_retention),
            encoding="utf-8",
            enqueue=True
        )
        # debug日志, 只记录日志级别为DEBUG的日志
        self.logger.add(
            sink=f"{settings.log_path}/app.debug.log",
            format=_trace_log_format,
            level=logging.DEBUG,
            filter=lambda record: record["level"].name == "DEBUG",
            rotation=settings.log_rotation,
            retention=timedelta(days=settings.log_retention),
            encoding="utf-8",
            enqueue=True
        )
        # error日志, 只记录日志级别为ERROR的日志
        self.logger.add(
            sink=f"{settings.log_path}/app.error.log",
            format=_trace_log_format,
            level=logging.ERROR,
            filter=lambda record: record["level"].name == "ERROR",
            rotation=settings.log_rotation,
            retention=timedelta(days=settings.log_retention),
            encoding="utf-8",
            enqueue=True
        )

    def get_logger(self):
        """
        获取配置好的日志对象

        :return: 配置好的loguru日志对象
        """
        return self.logger


# 创建日志配置实例
_logger_config = LoggerConfiguration()
# 日志对象实例，单例
logger = _logger_config.get_logger()


def print_startup_log():
    """ 打印启动日志 """
    logger.info(f"""
----------------------------------------------------------
项目 '{settings.app_name}' 启动成功! 访问链接:
Swagger文档: \t http://{settings.app_host}:{settings.app_port}{settings.swagger_docs_url}
OpenApi地址: \t http://{settings.app_host}:{settings.app_port}{settings.swagger_openapi_url}
----------------------------------------------------------
    """)
